#pragma once


namespace SD_Tools
{
	class Internal
	{
	private:
	public:
	private:
		static bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask);
	public:
		static void* DetourFunc(PBYTE src, const PBYTE dst, const int len = 5);
		static void* DetourFunc_s(PBYTE src, const PBYTE dst, const int len = 5);
		static void* DetourWithoutSaveFunc(PBYTE src, PBYTE dst, const int len = 5);
		static DWORD FindPattern(DWORD Address, DWORD Length, const BYTE* Mask1, const char * Mask2);
		static DWORD FindFullPattern(DWORD Address, DWORD Length, const BYTE* Mask, int PatternLength);
		static DWORD* GetVTableByObject(void* Object);
		static DWORD GetVTableFunctionCount(DWORD* VTable);
		static int VTableWalk(DWORD* VTable, DWORD Target, int MaxIndex);
		static DWORD DetourVTable(DWORD* VTable, DWORD Target, int Index);
		static DWORD DetourVTable_s(DWORD* VTable, DWORD Target, int Index);
		static DWORD DetourObjectVTable(void* Object, DWORD Target, int Index);
		static DWORD DetourObjectVTable_s(void* Object, DWORD Target, int Index);
		static void CloneVTable(DWORD* Target, DWORD* Source, int Size);
		static DWORD* ReplaceObjectVTable(void* Object, DWORD* NewVTable);
		static DWORD GetVTableFunction(DWORD* Vtable, int Index);
		static DWORD GetObjectVtableFunction(void* Object, int Index);
		static void MemCpySecure(void* Destination, void* Source, int Length);
		static DWORD GetModuleSize(HMODULE Module);
		template<typename T> static T* DerefPointer(DWORD Base, int Amount, ...);
		template<typename T> static T* DerefPointer_s(DWORD Base, int Amount, ...);
		template<typename T> static T* DerefPointer_s_s(DWORD Base, int Amount, ...);
		template<typename T> static T* MakePointer(void* ThisPtr, int Offset);
	};

	//************************************
	// Method:    DetourFunc
	// Profit:	  Detours a function
	// FullName:  SD_Tools::Internal::DetourFunc
	// Returns:   void*
	// Notes:
	// -Replaces the first 5 Bytes of a function with a jump to your function (0xE9 XXXXXXXX), left over Bytes will be Nopped (0x90)
	// -Return Value is a Pointer to a trampolin with the saved code
	// -len must be atleast 5 and must stop exactly on the end of an assembler code at the beginning of the to detour function
	// -since it does a code change, easily detected by most anticheats
	//************************************
	void* Internal::DetourFunc( PBYTE src, const PBYTE dst, const int len /*= 5*/ )
	{
		DWORD dwback;
		BYTE* jmp = (BYTE*)malloc(len+5);
		VirtualProtect(jmp, len+5, PAGE_EXECUTE_READWRITE, &dwback);
		VirtualProtect(src, len, PAGE_READWRITE, &dwback);
		memcpy(jmp, src, len);
		jmp += len;
		jmp[0] = 0xE9;
		*(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5;
		src[0] = 0xE9;
		*(DWORD*)(src+1) = (DWORD)(dst - src) - 5;
		for (int i = 5; i < len; i++)
		{
			src[i] = 0x90;
		}
		VirtualProtect(src, len, dwback, &dwback);
		return (jmp - len);
	}
	

	//************************************
	// Method:    DetourFunc
	// Profit:	  Detours a function
	// FullName:  SD_Tools::Internal::DetourFunc
	// Returns:   void*
	// Notes:
	// -Replaces the first 5 Bytes of a function with a jump to your function (0xE9 XXXXXXXX), left over Bytes will be Nopped (0x90)
	// -Return Value is a Pointer to a trampolin with the saved code
	// -len must be atleast 5 and must stop exactly on the end of an assembler code at the beginning of the to detour function
	// -since it does a code change, easily detected by most anticheats
	// -Fixes any existing detour or call
	//************************************
	void* Internal::DetourFunc_s( PBYTE src, const PBYTE dst, const int len /*= 5*/ )
	{
		DWORD dwback;
		BYTE* jmp = (BYTE*)malloc(len+5);
		VirtualProtect(jmp, len+5, PAGE_EXECUTE_READWRITE, &dwback);
		VirtualProtect(src, len, PAGE_READWRITE, &dwback);
		memcpy(jmp, src, len);
		if (jmp[0] == 0xE9 || jmp[0] == 0xE8)
		{
			DWORD* Temp = (DWORD*) jmp+1;
			*Temp += (DWORD) (src - jmp);
		}
		jmp += len;
		jmp[0] = 0xE9;
		*(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5;
		src[0] = 0xE9;
		*(DWORD*)(src+1) = (DWORD)(dst - src) - 5;
		for (int i = 5; i < len; i++)
		{
			src[i] = 0x90;
		}
		VirtualProtect(src, len, dwback, &dwback);
		return (jmp - len);
	}

	//************************************
	// Method:    DetourWithoutSaveFunc
	// Profit:	  Detours a function
	// FullName:  SD_Tools::Internal::DetourWithoutSaveFunc
	// Returns:   void*
	// Notes:
	// -Doesnt save the overwritten Bytes
	// -returns a pointer to the code after the patch
	//************************************
	void* Internal::DetourWithoutSaveFunc( PBYTE src, PBYTE dst, const int len /*= 5*/ )
	{
		DWORD dwback;
		VirtualProtect(src, len, PAGE_READWRITE, &dwback);
		src[0] = 0xE9;
		*(DWORD*)(src+1) = (DWORD)(dst - src) - 5;
		for (int i = 5; i < len; i++)
		{
			src[i] = 0x90;
		}
		VirtualProtect(src, len, dwback, &dwback);
		return (src + len);
	}

	bool Internal::bDataCompare(const BYTE* Buffer, const BYTE* Mask1, const char* Mask2)
	{
		for(;*Mask2;++Mask2,++Buffer,++Mask1)
		{
			if(*Mask2=='x' && *Buffer!=*Mask1 )
			{
				return false;
			}
		}
		return true;
	}

	//************************************
	// Method:    FindPattern
	// Profit:	  Finds a pattern
	// FullName:  SD_Tools::Internal::FindPattern
	// Returns:   DWORD
	// Notes:
	// -May crash if MaskSize + Length exceeds the Readable section and No Pattern is found
	//************************************
	DWORD Internal::FindPattern(DWORD Address, DWORD Length, const BYTE* Mask1, const char* Mask2)
	{
		for(DWORD i = 0; i < Length; i++)
		{
			if(bDataCompare((BYTE*)(Address+i), Mask1, Mask2))
			{
				return Address+i;
			}
		}
		return 0;
	}

	DWORD Internal::FindFullPattern( DWORD Address, DWORD Length, const BYTE* Mask, int PatternLength )
	{
		for (DWORD i = 0; i < Length; i++)
		{
			if (memcmp((void*) (Address+i), Mask, PatternLength) == 0)
			{
				return Address+i;
			}
		}
		return 0;
	}

	//************************************
	// Method:    GetVTableByObject
	// Profit:	  Gets the first Vtable of an Object
	// FullName:  SD_Tools::Internal::GetVTableByObject
	// Returns:   DWORD*
	// Notes:
	// -"return *(DWORD**)Object;"
	//************************************
	DWORD* Internal::GetVTableByObject( void* Object )
	{
		return *(DWORD**)Object;
	}

	//************************************
	// Method:    GetVTableFunctionCount
	// Profit:	  Gets the FunctionCount of a VTable
	// FullName:  SD_Tools::Internal::GetVTableFunctionCount
	// Returns:   DWORD
	// Notes:
	// -Iterates over all VTable pointers until the pointer doesnt points to an executable area
	//************************************
	DWORD Internal::GetVTableFunctionCount( DWORD* VTable )
	{
		MEMORY_BASIC_INFORMATION mem;
		DWORD i = -1;
		void** vmt = (void**) VTable;

		do
		{
			i++;
			VirtualQuery(vmt[i], &mem, sizeof(MEMORY_BASIC_INFORMATION) );
		} while(mem.Protect == PAGE_EXECUTE_READ || mem.Protect == PAGE_EXECUTE_READWRITE);

		return i;
	}

	//************************************
	// Method:    VTableWalk
	// Profit:	  Finds a TargetAddress in a VTable
	// FullName:  SD_Tools::Internal::VTableWalk
	// Returns:   int
	// Notes:
	// -None
	//************************************
	int Internal::VTableWalk(DWORD* VTable, DWORD Target, int MaxIndex)
	{
		int Found = -1;
		for (int i = 0; i < MaxIndex; i++)
		{
			if (VTable[i] == Target)
			{
				Found = i;
				break;
			}
		}
		return Found;
	}

	//************************************
	// Method:    DetourVTable
	// Profit:	  Replaces a VTable Entry
	// FullName:  SD_Tools::Internal::DetourVTable
	// Returns:   DWORD
	// Notes:
	// -Returns the old Entry
	// -Most VTables are Protected from writing - this function does NOT change this - use DetourVTable_s instead!
	//************************************
	DWORD Internal::DetourVTable(DWORD* VTable, DWORD Target, int Index)
	{
		DWORD Old = VTable[Index];
		VTable[Index] = Target;
		return Old;
	}

	//************************************
	// Method:    DetourVTable_s
	// Profit:	  Replaces a VTable Entry
	// FullName:  SD_Tools::Internal::DetourVTable_s
	// Returns:   DWORD
	// Notes:
	// -Returns the old Entry
	//************************************
	DWORD Internal::DetourVTable_s(DWORD* VTable, DWORD Target, int Index)
	{
		DWORD Old = VTable[Index];

		DWORD OldProc;
		VirtualProtect(&VTable[Index], 4, PAGE_EXECUTE_READWRITE, &OldProc);
		VTable[Index] = Target;
		VirtualProtect(&VTable[Index], 4, OldProc, &OldProc);

		return Old;
	}

	//************************************
	// Method:    DetourObjectVTable
	// Profit:	  Replaces a VTable Entry in the first VTable of the given Object
	// FullName:  SD_Tools::Internal::DetourObjectVTable
	// Returns:   DWORD
	// Notes:
	// -Most VTables are Protected from writing - this function does NOT change this - use DetourObjectVTable_s instead!
	//************************************
	DWORD Internal::DetourObjectVTable(void* Object, DWORD Target, int Index)
	{
		DWORD* VTable = *(DWORD**)Object;
		return DetourVTable(VTable, Target, Index);
	}

	//************************************
	// Method:    DetourObjectVTable_s
	// Profit:	  Replaces a VTable Entry in the first VTable of the given Object
	// FullName:  SD_Tools::Internal::DetourObjectVTable_s
	// Returns:   DWORD
	// Notes:
	// -Returns the old Entry
	//************************************
	DWORD Internal::DetourObjectVTable_s(void* Object, DWORD Target, int Index)
	{
		DWORD* VTable = *(DWORD**)Object;
		return DetourVTable_s(VTable, Target, Index);
	}

	//************************************
	// Method:    CloneVTable
	// Profit:	  Clones a VTable
	// FullName:  SD_Tools::Internal::CloneVTable
	// Returns:   void
	// Notes:
	// -Copys the Source VTable to the TargetVTable
	// -Size is the number of Functions
	//************************************
	void Internal::CloneVTable( DWORD* Target, DWORD* Source, int Size )
	{
		memcpy(Target, Source, Size*4);
	}

	//************************************
	// Method:    ReplaceObjectVTable
	// Profit:	  Replaces an ObjectVTable with the given VTable
	// FullName:  SD_Tools::Internal::ReplaceObjectVTable
	// Returns:   DWORD*
	// Notes:
	// -Returns the Old VTable Pointer
	//************************************
	DWORD* Internal::ReplaceObjectVTable(void* Object, DWORD* NewVTable)
	{
		DWORD* Old = *(DWORD**)Object;
		*(DWORD**)Object = NewVTable;
		return Old;
	}

	DWORD Internal::GetVTableFunction( DWORD* VTable, int Index )
	{
		return VTable[Index];
	}

	DWORD Internal::GetObjectVtableFunction( void* Object, int Index )
	{
		return (*((DWORD**)Object))[Index];
	}

	//************************************
	// Method:    MemCpySecure
	// Profit:	  Copies Memory
	// FullName:  SD_Tools::Internal::MemCpySecure
	// Returns:   void
	// Notes:
	// -Calls VirtualProtect on Destination to make sure Write rights are given - sets it back to old state after copying
	//************************************
	void Internal::MemCpySecure( void* Destination, void* Source, int Length )
	{
		unsigned long OldProtection;
		VirtualProtect(Destination, Length, PAGE_EXECUTE_READWRITE, &OldProtection);
		memcpy(Destination, Source, Length);
		VirtualProtect(Destination, Length, OldProtection, NULL);
	}

	DWORD Internal::GetModuleSize( HMODULE Module )
	{
		MODULEINFO MyModuleInfo = {0};
		GetModuleInformation(GetCurrentProcess(), Module, &MyModuleInfo, sizeof(MyModuleInfo));
		return MyModuleInfo.SizeOfImage;
	}

	//************************************
	// Method:    DerefPointer
	// Profit:	  Derefs a multipointer
	// FullName:  SD_Tools::Internal<T>::DerefPointer
	// Returns:   T*
	// Notes:
	// -Does NOT check for any errors like NULL-Pointers etc. - use DerefPointer_s or DerefPointer_s_s instead!
	//************************************
	template<typename T> T* SD_Tools::Internal::DerefPointer(DWORD Base, int Amount, ...)
	{
		DWORD Result = Base;
		va_list Args;

		va_start(Args, Amount);
		for (int i = 0; i < Amount; i++)
		{
			Result = *(DWORD*)Result + va_arg(Args, DWORD);
		}
		va_end(Args);

		return (T*) Result;
	}

	//************************************
	// Method:    DerefPointer_s
	// Profit:	  Derefs a multipointer
	// FullName:  SD_Tools::Internal<T>::DerefPointer_s
	// Returns:   T*
	// Notes:
	// -Only checks for NULL pointers
	//************************************
	template<typename T> T* SD_Tools::Internal::DerefPointer_s(DWORD Base, int Amount, ...)
	{
		DWORD Result = Base;
		va_list Args;

		va_start(Args, Amount);
		for (int i = 0; i < Amount; i++)
		{
			Result = *(DWORD*)Result + va_arg(Args, DWORD);
			if (!Result)
			{
				return NULL;
			}
		}
		va_end(Args);

		return (T*) Result;
	}

	//************************************
	// Method:    DerefPointer_s_s
	// Profit:	  Derefs a multipointer
	// FullName:  SD_Tools::Internal<T>::DerefPointer_s_s
	// Returns:   T*
	// Notes:
	// -Checks for NULL and for BadReadPtrs
	//************************************
	template<typename T> T* SD_Tools::Internal::DerefPointer_s_s(DWORD Base, int Amount, ...)
	{
		DWORD Result = Base;
		va_list Args;

		va_start(Args, Amount);
		for (int i = 0; i < Amount; i++)
		{
			Result = *(DWORD*)Result + va_arg(Args, DWORD);
			if (!Result || IsBadReadPtr((void*) Result, 4))
			{
				return NULL;
			}
		}
		va_end(Args);

		return (T*) Result;
	}

	//************************************
	// Method:    MakePointer
	// Profit:	  Makes a T* Pointer of an Object with Offset
	// FullName:  SD_Tools::Internal<T>::MakePointer
	// Returns:   T*
	// Notes:
	// -None
	//************************************
	template<typename T> T* SD_Tools::Internal::MakePointer( void* ThisPtr, int Offset )
	{
		return (T*) ((DWORD)ThisPtr + Offset)
	}
}


